package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.service.CartInfoAsyncService;
import com.atguigu.gmall.cart.service.CartInfoService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.product.client.common.constant.RedisConst;
import com.atguigu.gmall.product.client.common.util.AuthContextHolder;
import com.atguigu.gmall.product.client.model.cart.CartInfo;
import com.atguigu.gmall.product.client.model.product.SkuInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@SuppressWarnings("all")  //只适合当前类  取消波浪线
@Service
public class CartInfoServiceImpl implements CartInfoService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private CartInfoAsyncService cartInfoAsyncService;

    //加入购物车
    @Override
    public CartInfo addCart(Long skuId, Integer skuNum) {
        //真实用户或临时用户id
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //获取真实用户id
        String userId = AuthContextHolder.getUserId(request);
        if (StringUtils.isEmpty(userId)){
            //临时用户Id
            userId=AuthContextHolder.getUserTempId(request);
        }

        //1. 写redis 同步   五大数据类型  string类型 值类型; 一般使用的是skuId token
        //这次存放在redis中使用的Hash类型  也叫散列类型
        //参数1 h   Map h = new HashMap<>()   用户id
        //参数2 hk  h.put(k,)                 skuId
        //参数3 hv  h.put(k,v)                CartInfo
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        String cacheHk = skuId.toString();
        //1.1 查询 redis
        CartInfo cartInfo = (CartInfo) redisTemplate.opsForHash().get(cacheH, cacheHk);
        if (cartInfo != null){
            //已经添加过一次购物车  再次添加 直接添加数量
            cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);  //设置数量 = 之前数量 +  当前数量
            //设置选中状态  添加当前商品 之前没有选中的商品需要勾选  勾选了的商品还需要是勾选的
            cartInfo.setIsChecked(CartInfo.IsChecked);

            //2. 写mysql 异步  更新数据到数据库  对redis的备份
            cartInfoAsyncService.updateByIdAsync(cartInfo);
        }else{
            //第一次添加购物车  添加
            cartInfo = new CartInfo();
            //商品Id
            cartInfo.setSkuId(skuId);
            //数量
            cartInfo.setSkuNum(skuNum);
            //库存信息对象
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            //用户Id
            cartInfo.setUserId(userId);
            // 放入购物车时的价格
            cartInfo.setCartPrice(skuInfo.getPrice());
            //图片
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            //标题
            cartInfo.setSkuName(skuInfo.getSkuName());
            //商品的实时价格
            cartInfo.setSkuPrice(skuInfo.getPrice());

            //2. 写mysql 异步  保存数据到数据库  对redis的备份
            cartInfoAsyncService.insertAsync(cartInfo);


        }
        //存放在缓存
        redisTemplate.opsForHash().put(cacheH,cacheHk,cartInfo);
        //设置过期时间  本次就不在设置

        return cartInfo;
    }


    //查询新添加的购物车商品
    @Override
    public CartInfo toCart(Long skuId) {
        //真实用户或临时用户id
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //获取真实用户id
        String userId = AuthContextHolder.getUserId(request);
        if (StringUtils.isEmpty(userId)){
            //临时用户Id
            userId=AuthContextHolder.getUserTempId(request);
        }
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        String cacheHk = skuId.toString();
        return (CartInfo) redisTemplate.opsForHash().get(cacheH,cacheHk);
    }

    //查询当前用户购物车的集合  (当前用户)
    //当前用户 : 临时用户
    //当前用户 : 真实用户
    //当前用户 : 真实用户 + 临时用户
    @Override
    public List<CartInfo> cartList(String userId, String userTempId) {
        //判断
        if (!StringUtils.isEmpty(userId)){
            //有真实用户
            if (!StringUtils.isEmpty(userTempId)){
                //有临时用户     购物车合并完返回购物车集合
                return mergeCartList(userId,userTempId);
            }else{
                //无临时用户  返回真实用户的购物车集合
                return getCartList(userId);
            }

        }else{
            //无真实用户
            if (!StringUtils.isEmpty(userTempId)){
                //有临时用户  返回临时用户的购物车集合
                return getCartList(userTempId);
            }
        }
        //啥也没有
        return  null;

    }

    //购物车页面  选中复选框
    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        //真实用户或临时用户id
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //获取真实用户id
        String userId = AuthContextHolder.getUserId(request);
        //更新缓存  同步
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        String cacheHk = skuId.toString();
        CartInfo cartInfo = (CartInfo) redisTemplate.opsForHash().get(cacheH, cacheHk);
        cartInfo.setIsChecked(isChecked);
        redisTemplate.opsForHash().put(cacheH,cacheHk,cartInfo);
        //更新DB  异步
        cartInfoAsyncService.updateByIdAsync(cartInfo);
    }

    //查询商品清单
/*
    @Override
    public List<CartInfo> getCartListChecked(String userId) {
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cacheH);
        return cartInfoList.stream().filter(cartInfo -> cartInfo.getIsChecked()==1)
                .collect(Collectors.toList());
    }
*/
    //查询商品清单
    @Override
    public List<CartInfo> getCartListChecked(String userId) {
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cacheH);
        //实时价格  如果加了Canal就没有必须写实时价格
        return cartInfoList.stream().filter(cartInfo -> cartInfo.getIsChecked() == 1).collect(Collectors.toList());
    }


    //开始合并购物车集合
    private List<CartInfo> mergeCartList(String userId, String userTempId) {
        //1. 获取真实用户
        List<CartInfo> cartListUserId = getCartList(userId);
        //2. 获取临时用户
        List<CartInfo> cartListUserTempId = getCartList(userTempId);

        //3. 判断
        if (!CollectionUtils.isEmpty(cartListUserId)){
            //有 真实用户的购物车集合
            if (!CollectionUtils.isEmpty(cartListUserTempId)){
                //有 临时用户的购物车集合   //合并 真+临 用户购物车集合
                //map k:skuId   v:cartInfo
                Map<Long, CartInfo> cartInfoMapUserId = cartListUserId.stream()
                        .collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));

                cartListUserTempId.stream().forEach(cartInfoUserTempId -> {
                    //1. 判断 临时购物车  是否在真实购物车集合中存在
                    CartInfo cartInfoUserId = cartInfoMapUserId.get(cartInfoUserTempId.getSkuId()); //真实用户购物车
                    if (cartInfoUserId != null){
                        //1.1 存在 追加数量  设置选中   临时用户购物车  + 真实用户购物车
                        cartInfoUserId.setSkuNum(cartInfoUserTempId.getSkuNum() + cartInfoUserId.getSkuNum());
                        cartInfoUserId.setIsChecked(CartInfo.IsChecked);  //设置为选中

                        //添加redis
                        //真实用户的key
                        String cacheHUserId = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
                        redisTemplate.opsForHash().put(
                                cacheHUserId,
                                cartInfoUserId.getSkuId().toString(),
                                cartInfoUserId);

                        //异步更新DB
                        cartInfoAsyncService.updateByIdAsync(cartInfoUserId);
                    }else{
                        //1.2 不存在  添加新购物车   添加真实用户购物车
                        cartInfoMapUserId.put(cartInfoUserTempId.getSkuId(),cartInfoUserTempId);

                        //添加redis 异步更新DB
                        cartInfoUserTempId.setUserId(userId);
                        //真实用户的key
                        String cacheHUserId = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
                        redisTemplate.opsForHash().put(
                                cacheHUserId,
                                cartInfoUserTempId.getSkuId().toString(),
                                cartInfoUserTempId);

                        //异步添加DB
                        cartInfoAsyncService.insertAsync(cartInfoUserTempId);
                    }
                });
                //DB中统一删除临时用户的购物车集合
                cartInfoAsyncService.deleteByUserTempId(userTempId);

                //异步更新DB


                //整体将临时用户的购物车在redis中删掉
                //临时用户的key
                String cacheHUserTempId = RedisConst.USER_KEY_PREFIX + userTempId + RedisConst.USER_CART_KEY_SUFFIX;
                //删除缓存中的临时用户
                redisTemplate.delete(cacheHUserTempId);

                //cartInfoMapUserId  将map类型转到list集合
               return new ArrayList(cartInfoMapUserId.values());
            }else{
                //没有  临时用户的购物车集合
                return cartListUserId;   //没有临时的直接返回真实用户
            }
        }else{
            //没有 真实用户的购物车集合

            if (!CollectionUtils.isEmpty(cartListUserTempId)){
                //临时用户的key
                String cacheHUserTempId = RedisConst.USER_KEY_PREFIX + userTempId + RedisConst.USER_CART_KEY_SUFFIX;
                //真实用户的key
                String cacheHUserId = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;

                //List<CartInfo> cartListUserTempId
                //cartInfoMap  是一个真实用户的对象
                Map<String, CartInfo> cartInfoMap = cartListUserTempId.stream().map(cartInfo -> {
                    //将临时用户id设置为真实用户id
                    cartInfo.setUserId(userId);
                    return cartInfo;
                }).collect(Collectors
                        .toMap(cartInfo -> cartInfo.getSkuId().toString(), //config配置类 k值是需要为string类型的 由long转为string类型的
                                cartInfo -> cartInfo));
                //添加真实用户的缓存
                redisTemplate.opsForHash().putAll(cacheHUserId,cartInfoMap);  //参数一: 真实用户的key  参数二: 真实用户
                //删除缓存中的临时用户
                redisTemplate.delete(cacheHUserTempId);

                cartInfoAsyncService.updateByIdAsyncExt(userTempId,userId);
                //有临时用户的购物车集合
                return cartListUserTempId;
            }
        }
        //啥也没有
        return null;

    }

    //根据用户id获取购物车集合
    private List<CartInfo> getCartList(String userId) {
        String cacheH = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        //有可能更新一下实时价格   解决:登录前的价格和登录后的价格不一样
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cacheH);
        //排序    到购物车结算  是按照添加的数量进行降序排序
        return cartInfoList.stream().sorted((o1, o2) -> {
            return o2.getSkuNum() - o1.getSkuNum();
        }).collect(Collectors.toList());
    }


    //去购物车结算  再次查询实时价格 页面回显的时候 价格就正确了

}
